#include "echo.h"
#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
#include <boost/bind.hpp>
#include <stdio.h>

EchoServer::EchoServer(muduo::net::EventLoop *loop, const muduo::net::InetAddress &listenAddr, int idleSeconds) 
	: server_(loop, listenAddr, "EchoServer"),
	  connectionBuckets_(idleSeconds) {
	server_.setConnectionCallback(boost::bind(&EchoServer::OnConnection, this, _1));
	server_.setMessageCallback(boost::bind(&EchoServer::OnMessage, this, _1, _2, _3));
	loop->runEvery(1.0, boost::bind(&EchoServer::OnTimer, this));
	connectionBuckets_.resize(idleSeconds);
	DumpConnectionBuckets();
}

void
EchoServer::Start() {
	server_.start();
}

void
EchoServer::OnConnection(const muduo::net::TcpConnectionPtr &conn) {
	LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
	         << conn->localAddress().toIpPort() << " is "
	         << (conn->connected() ? "UP" : "DOWN");
	
	if (conn->connected()) {
		EntryPtr entry(new Entry(conn));
		connectionBuckets_.back().insert(entry);
		DumpConnectionBuckets();

		WeakEntryPtr weakEntry(entry);
		conn->setContext(weakEntry);
	} else {
		assert(!conn->getContext().empty());
		WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));
		LOG_INFO << "Entry use_count = " << weakEntry.use_count();
	}
}

void
EchoServer::OnMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp time) {
	muduo::string msg(buf->retrieveAllAsString());
	LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
	         << "data received at " << time.toString();
	conn->send(msg);

	assert(!conn->getContext().empty());
	WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));
	EntryPtr entry(weakEntry.lock());
	if (entry) {
		connectionBuckets_.back().insert(entry);
		DumpConnectionBuckets();
	}
}

void
EchoServer::OnTimer() {
	connectionBuckets_.push_back(Bucket());
	DumpConnectionBuckets();
}

void
EchoServer::DumpConnectionBuckets() const {
	LOG_INFO << "bucket size " << connectionBuckets_.size();
	int idx = 0;
	for (WeakConnectionList::const_iterator bucketI = connectionBuckets_.begin();
		 bucketI != connectionBuckets_.end();
		 ++bucketI, ++idx) {
		const Bucket &bucket = *bucketI;
		printf("[%d] len = %zd\n", idx, bucket.size());
		
		for (Bucket::const_iterator it = bucket.begin();
			 it != bucket.end();
			 ++it) {
			bool connectionDead = (*it)->weakConn_.expired();
		printf("%p(%ld)%s\n", boost::get_pointer(*it), it->use_count(),
			                  connectionDead ? "DEAD" : "");
		}
	}
}
